using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace SpaceWolf
{
    public enum CameraMode { Fixed, Tracking, FirstPerson, ThirdPerson, TopDownFixed, TopDownCentred };
    /// <summary>
    /// First person camera component for the demos, rotated by mouse.
    /// </summary>
    public class Camera
    {
        
        float fMouseRot = 0;
        public static CameraMode currentCameraMode = CameraMode.FirstPerson;

        public Vector3 Up;

        #region Variables
        private BoundingFrustum frustum;
        private Vector3 cameraVelocity = Vector3.Zero;
        private Vector3 currentCameraPosition = Vector3.Zero;
        private KeyboardState currentKeyboardState;
        private KeyboardState previousKeyboardState;

        // Set the position of the model in world space, and set the rotation.
        private Vector3 modelPosition;
        private float modelRotation = 0.0f;

        // Set the position of the camera in world space, for our view matrix.
        private Vector3 cameraFixedPosition = new Vector3(-10.0f, 0.0f, 00.0f);

        // 1st Person camera position relative to player model
        private Vector3 cameraFirstPersonPosition = new Vector3(0f, 0f,10f);

        // 3rd Person camera position relative to player model
        private Vector3 cameraThirdPersonPosition = new Vector3(0.0f, 0.0f, -20);

        // Top Down camera position relative to player model
        private Vector3 cameraTopDownPosition = new Vector3(.1f, 50, 0);

        // Matrices required to correctly display our scene
        private Matrix currentView;
        private Matrix currentProjection;
        private Matrix orientation;

        // Field of view of the camera in radians (pi/4 is 45 degrees).
        private float fieldOfView = MathHelper.ToRadians(45.0f);
        private float cameraStiffness = 1800.0f;
        private float cameraDamping = 600.0f;
        private float cameraMass = 50.0f;
        //private MouseState previousMouseState;
        #endregion

        #region Properties
        public Matrix Orientation
        {
            set
            {
                this.orientation = value;
            }
            get
            {
                return this.orientation;
            }
        }

        public Vector3 Target
        {
            set
            {
                this.modelPosition = value;
            }
            get
            {
                return this.modelPosition;
            }
        }

        public Vector3 Position
        {
            set
            {
                this.cameraThirdPersonPosition = value;
            }
            get
            {
                return this.cameraThirdPersonPosition;
            }
        }

        public Matrix View
        {
            get
            {
                return this.currentView;
            }
        }

        public Matrix Projection
        {
            get
            {
                return this.currentProjection;
            }
        }

        public BoundingFrustum Frustum
        {
            get
            {
                return this.frustum;
            }
        }
        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="game">instance of game class</param>
        public Camera()
        {
            currentProjection = BaseGame.ProjectionMatrix;
            //orientation.Up = Vector3.Up;
        }

        public void Update(GameTime aGameTime)
        {
            //if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            //  this.Exit();

            // Get some input.
            UpdateInput(aGameTime);

            // Update audioEngine.
            switch (currentCameraMode)
            {
                case CameraMode.Fixed:
                    UpdateFixedCamera();
                    break;

                case CameraMode.Tracking:
                    UpdateTrackingCamera();
                    break;

                case CameraMode.FirstPerson:
                    UpdateFirstPersonCamera();
                    break;

                case CameraMode.ThirdPerson:
                    UpdateThirdPersonCamera((float)aGameTime.ElapsedGameTime.TotalSeconds);
                    break;

                case CameraMode.TopDownFixed:
                    UpdateTopDownFixedCamera();
                    break;

                case CameraMode.TopDownCentred:
                    UpdateTopDownCenteredCamera();
                    break;
            }
            frustum = new BoundingFrustum(currentView * currentProjection);
            
            BaseGame.ViewMatrix = currentView;
            //base.Update(aGameTime);
        }

        protected void UpdateInput(GameTime aGameTime)
        {
            // Get the game pad state.
            // Get the Keyboard state.
            previousKeyboardState = currentKeyboardState;
            currentKeyboardState = Keyboard.GetState();

            if (currentKeyboardState != null)
            {

                // Toggle the state of the camera.
                if ((currentKeyboardState.IsKeyUp(Keys.F1) && previousKeyboardState.IsKeyDown(Keys.F1)))
                {
                    currentCameraMode++;
                    currentCameraMode = (CameraMode)((int)currentCameraMode % 6);
                }
            }
            Helper.Fonts.WriteText(10, 20, currentCameraMode.ToString());
            #region mouse
            //MouseState currentMouseState = Mouse.GetState();

            //if (currentMouseState != previousMouseState)
            //{
            //    //bUpdateMouse = true;
            //    if (currentMouseState.X != previousMouseState.X)
            //    {
            //        fMouseRot -= 1 / 80.0f * (currentMouseState.X - previousMouseState.X);
            //        //moveVector.X = 1f;
            //    }

            //    //if (currentMouseState.Y != previousMouseState.Y)
            //    //{
            //    //    //if (Math.Abs(RotationAngles.X - amountOfMovement / 80.0f * (currentMouseState.Y - previousMouseState.Y)) < 0.7f)

            //    //    RotationAngles.X -= amountOfMovement / 80.0f * (currentMouseState.Y - previousMouseState.Y);
            //    //}

            //    Mouse.SetPosition(BaseGame.Width / 2, BaseGame.Height / 2);
            //    previousMouseState = Mouse.GetState();

            //}
            #endregion
        }

        private bool cameraSpringEnabled = true;

        #region Private Methods
        void UpdateWorldViewProjection(Vector3 aCameraPosition, Vector3 aCameraTarget)
        {
            //currentWorld = Matrix.CreateRotationY(modelRotation) * Matrix.CreateTranslation(modelPosition);
            //aCameraPosition = Vector3.Transform(aCameraPosition, Matrix.CreateRotationY(modelRotation));
            //Vector3 CamUP = Vector3.Transform(Vector3.Up, orientation);

            currentView = Matrix.CreateLookAt(aCameraPosition, aCameraTarget, Up);

            //string str = string.Format("Camera pos: {0} target: {1}", aCameraPosition, aCameraTarget);
            //Helper.Fonts.WriteText(300, 10 , str);
        }

        void UpdateFixedCamera()
        {
            // Set up our world matrix, view matrix and projection matrix.
            UpdateWorldViewProjection(cameraFixedPosition, Vector3.Zero);
        }

        void UpdateTrackingCamera()
        {
            // Create a vector pointing the direction the camera is facing.  
            Vector3 transformedReference = Vector3.Transform(modelPosition, Matrix.Identity);

            // Calculate the position the camera is looking at.
            Vector3 cameraLookat = transformedReference + modelPosition;

            // Set up our world matrix, view matrix and projection matrix.
            UpdateWorldViewProjection(cameraFixedPosition, cameraLookat);
        }

        void UpdateFirstPersonCamera()
        {

            // Create a vector pointing the direction the camera is facing.
            Vector3 transformedReference = Vector3.Transform(modelPosition + cameraFirstPersonPosition, orientation);

            //Vector3 newModelPosition = Vector3.Transform(modelPosition + cameraFirstPersonPosition, orientation);
            // Calculate the position the camera is looking from.
            currentCameraPosition = transformedReference;

            // Set up our world matrix, view matrix and projection matrix.
            UpdateWorldViewProjection(modelPosition, currentCameraPosition);
        }

        void UpdateThirdPersonCamera(float aElapsed)
        {

            // Create a vector pointing the direction the camera is facing.
            Vector3 transformedReference = Vector3.Transform(cameraThirdPersonPosition, orientation);

            if (cameraSpringEnabled)
            {
                // Calculate the position where we would like the camera to be looking from.
                Vector3 desiredPosition = transformedReference + modelPosition;

                // Calculate spring force            
                Vector3 stretch = currentCameraPosition - desiredPosition;
                Vector3 force = -cameraStiffness * stretch - cameraDamping * cameraVelocity;

                // Apply acceleration 
                Vector3 acceleration = force / cameraMass;
                cameraVelocity += acceleration * aElapsed;

                // Apply velocity
                currentCameraPosition += cameraVelocity * aElapsed;
            }
            else
            {
                // Calculate the position the camera is looking from.
                currentCameraPosition = transformedReference + modelPosition;
            }

            Vector3 ForwardOffset = new Vector3(0, 0, 5);
            Vector3 ForwardDistance = Vector3.Zero; // Position camera in space    
            Vector3 newPosition = currentCameraPosition;
            //// Position camera should look at    
            ForwardDistance = new Vector3(ForwardOffset.X, ForwardOffset.Y, cameraThirdPersonPosition.Z - ForwardOffset.Z);
            ForwardDistance = Vector3.Transform(ForwardDistance, Matrix.CreateRotationY(fMouseRot));
            //// Keeps camera behind its target    
            newPosition = Vector3.Transform(newPosition, Matrix.CreateRotationY(modelRotation));
            //// Keeps camera "attached" to its target    
            newPosition = Vector3.Transform(Position, Matrix.CreateTranslation(modelPosition + ForwardDistance));
            //ForwardVector = Vector3.Normalize(aCameraTarget + ForwardDistance - Position);
            //currentView = Matrix.CreateLookAt(newPosition, aCameraTarget, Vector3.Up);

            // Set up our world matrix, view matrix and projection matrix.
            UpdateWorldViewProjection(newPosition, modelPosition);
        }

        void UpdateTopDownFixedCamera()
        {
            // Set up our world matrix, view matrix and projection matrix.
            UpdateWorldViewProjection(cameraTopDownPosition, Vector3.Zero);
        }

        void UpdateTopDownCenteredCamera()
        {
            Matrix rotationMatrix = Matrix.CreateRotationY(modelRotation);

            // Create a vector pointing the direction the camera is facing.
            Vector3 transformedReference = Vector3.Transform(cameraTopDownPosition, rotationMatrix);

            // Calculate the position the camera is looking from.
            currentCameraPosition = transformedReference + modelPosition;

            // Set up our world matrix, view matrix and projection matrix.
            UpdateWorldViewProjection(currentCameraPosition, modelPosition);
        }
        #endregion
    }
}

